#include <stdlib.h>
#include <string.h>
#include "queue.h"


queue_t *queue_new(int maxsize)
{
    int size = sizeof(queue_t) + maxsize * sizeof(void *);
    queue_t *q = malloc(size);
    if (q == NULL)
        goto failed;

    memset(q, 0, size);
    q->maxsize = maxsize;
    pthread_mutex_init(&q->mutex, NULL);
    pthread_cond_init(&q->not_empty, NULL);
    pthread_cond_init(&q->not_full, NULL);
failed:
    return q;
}

void queue_del(queue_t *q)
{
    free(q);
}

static inline int is_full(queue_t *q)
{
    return (q->size == q->maxsize);
}

static inline int is_empty(queue_t *q)
{
    return (q->size == 0);
}

void queue_put(queue_t *q, void *data)
{
    pthread_mutex_lock(&q->mutex);
    while (is_full(q)) {
        pthread_cond_wait(&q->not_full, &q->mutex);
    }
    q->data[q->rear] = data;
    q->rear = (q->rear + 1) % q->maxsize;
    q->size++;
    pthread_cond_signal(&q->not_empty);
    pthread_mutex_unlock(&q->mutex);
}

void *queue_get(queue_t *q)
{
    pthread_mutex_lock(&q->mutex);
    while (is_empty(q)) {
        pthread_cond_wait(&q->not_empty, &q->mutex);
    }
    void *data = q->data[q->front];
    q->front = (q->front + 1) % q->maxsize;
    q->size--;
    pthread_cond_signal(&q->not_full);
    pthread_mutex_unlock(&q->mutex);
    return data;
}

int queue_size(queue_t *q)
{
    int size;
    pthread_mutex_lock(&q->mutex);
    size = q->size;
    pthread_mutex_unlock(&q->mutex);
    return size;
}
